/**
 * 
 */
package com.ubo.visu.painter.association;

import javafx.beans.binding.StringBinding;
import javafx.scene.paint.Color;
import javafx.scene.shape.SVGPath;
import javafx.scene.shape.SVGPathBuilder;
import javafx.scene.text.Text;
import javafx.scene.text.TextBuilder;
import javafx.scene.transform.Rotate;

import com.ubo.util.Duo;
import com.ubo.visu.model.EModelAssociationType;
import com.ubo.visu.model.ModelAssociation;
import com.ubo.visu.painter.UtilsSize;
import com.ubo.visu.painter.node.INodePainter;
import com.ubo.visu.util.FxUtil;
import com.ubo.visu.util.PropertiesManager;

/**
 * @author saludegu
 * 
 */
public class LineAssociationPainter extends ALinePainter {

	SVGPath svgPath2;

	public LineAssociationPainter(INodePainter painterFrom,
			INodePainter painterTo) {
		super(painterFrom, painterTo);
	}

	/**
	 * @see com.ubo.visu.painter.association.IAssociationPainter#paint(com.ubo.visu.model.ModelAssociation)
	 */
	@Override
	public void paint(final ModelAssociation modelAssociation) {
		Duo<Integer, Integer> startPos = getPosition(mPainterFrom, modelAssociation.getNodeFrom());
		Duo<Integer, Integer> endPos = getPosition(mPainterTo, modelAssociation.getNodeTo());
		Duo<Duo<Double, Double>, Duo<Double, Double>> realPos = getPosition(
				startPos, endPos, modelAssociation);

		double coefDir = ((realPos.getB().getB() - realPos.getA().getB()) / (realPos
				.getB().getA() - realPos.getA().getA()));

		double posX = (realPos.getB().getA() - realPos.getA().getA()) * 1;
		double posY = coefDir * (posX);

		double secondPosX = (realPos.getB().getA() - realPos.getA().getA())
				- posX;
		double secondPosY = coefDir * (secondPosX);

		String content = "M" + realPos.getA().getA() + " "
				+ realPos.getA().getB() + "l" + posX + " " + posY;

		svgPath2 = SVGPathBuilder
				.create()
				.stroke(Color.web(PropertiesManager.getInstance().getProperty(
						PROPERTY_ASSOCIATION_COLOR)))
				.content(
						"M" + posX + " " + posY + "l" + secondPosX + " "
								+ secondPosY)
				.strokeWidth(2)
				.transforms(new Rotate(45, secondPosX, secondPosY))
				.fill(Color.web(PropertiesManager.getInstance().getProperty(
						PROPERTY_ASSOCIATION_COLOR))).build();

		final Text newTextCardFrom = TextBuilder
				.create()
				.fill(Color.web(PropertiesManager.getInstance().getProperty(
						PROPERTY_NODE_TEXT_COLOR)))
				.text(modelAssociation.getCardFrom()).build();

		final Text newTextCardTo = TextBuilder
				.create()
				.fill(Color.web(PropertiesManager.getInstance().getProperty(
						PROPERTY_NODE_TEXT_COLOR)))
				.text(modelAssociation.getCardTo()).build();

		newTextCardTo.setLayoutX(getFromCardPos(realPos.getA(), realPos.getB(),
				modelAssociation).getA());
		newTextCardTo.setLayoutY(getFromCardPos(realPos.getA(), realPos.getB(),
				modelAssociation).getB());
		newTextCardFrom.setLayoutX(getToCardPos(realPos.getA(), realPos.getB(),
				modelAssociation).getA());
		newTextCardFrom.setLayoutY(getToCardPos(realPos.getA(), realPos.getB(),
				modelAssociation).getB());

		final SVGPath svgPath = SVGPathBuilder
				.create()
				.stroke(Color.web(PropertiesManager.getInstance().getProperty(
						PROPERTY_ASSOCIATION_COLOR)))
				.content(content)
				.strokeWidth(2)
				.fill(Color.web(PropertiesManager.getInstance().getProperty(
						PROPERTY_ASSOCIATION_COLOR))).build();
		if (modelAssociation.getAssociationType().equals(
				EModelAssociationType.IMPLEMENTS)) {
			svgPath.getStrokeDashArray().addAll(10.0, 10.0);
		}

		StringBinding stringBinding = new StringBinding() {
			{
				super.bind(mPainterFrom.getContent().layoutXProperty(),
						mPainterFrom.getContent().layoutYProperty(), mPainterTo
								.getContent().layoutXProperty(), mPainterTo
								.getContent().layoutYProperty());
			}

			@Override
			protected String computeValue() {
				Duo<Integer, Integer> startPos = getPosition(mPainterFrom, modelAssociation.getNodeFrom());
				Duo<Integer, Integer> endPos = getPosition(mPainterTo, modelAssociation.getNodeTo());
				Duo<Duo<Double, Double>, Duo<Double, Double>> realPos = getPosition(
						startPos, endPos, modelAssociation);

				double posX = (realPos.getB().getA() - realPos.getA().getA());
				double posY = (realPos.getB().getB() - realPos.getA().getB());

				String content = "M" + realPos.getA().getA() + " "
						+ realPos.getA().getB() + "l" + posX + " " +posY;

				newTextCardTo.setLayoutX(getFromCardPos(realPos.getA(),
						realPos.getB(), modelAssociation).getA());
				newTextCardTo.setLayoutY(getFromCardPos(realPos.getA(),
						realPos.getB(), modelAssociation).getB());
				newTextCardFrom.setLayoutX(getToCardPos(realPos.getA(),
						realPos.getB(), modelAssociation).getA());
				newTextCardFrom.setLayoutY(getToCardPos(realPos.getA(),
						realPos.getB(), modelAssociation).getB());
				return content;
			}
		};
		svgPath.contentProperty().bind(stringBinding);
		FxUtil.runOnFx(new Runnable() {

			@Override
			public void run() {
				mContainer.getChildren().addAll(svgPath, newTextCardFrom,
						newTextCardTo);
			}
		});
	}

	/**
	 * Renvoie la position de la cardinalité de fin.
	 * 
	 * @param startPos
	 * @param endPos
	 * @return
	 */
	protected Duo<Double, Double> getToCardPos(Duo<Double, Double> startPos,
			Duo<Double, Double> endPos, ModelAssociation assoc) {
		double posX;
		double posY;
		double deltaX = PropertiesManager.getInstance().getDoubleProperty(
				PROPERTY_RECTANGLE_WIDTH) / 2;
		double deltaYTo = UtilsSize.computeRectangleHeight(assoc.getNodeTo());

		// Placement horizontal.
		if (endPos.getA() <= startPos.getA()) {
			posX = endPos.getA() - deltaX;
		} else {
			posX = endPos.getA() + deltaX;
		}
		// Placement vertical.
		if (endPos.getB() <= startPos.getB()) {
			posY = endPos.getB() + deltaYTo;
		} else {
			posY = endPos.getB() - deltaYTo;
		}
		return new Duo<Double, Double>(posX, posY);
	}

	/**
	 * Renvoie la position de la cardinalité de départ.
	 * 
	 * @param startPos
	 * @param endPos
	 * @return
	 */
	protected Duo<Double, Double> getFromCardPos(Duo<Double, Double> startPos,
			Duo<Double, Double> endPos, ModelAssociation assoc) {
		double posX;
		double posY;
		double deltaX = PropertiesManager.getInstance().getDoubleProperty(
				PROPERTY_RECTANGLE_WIDTH) / 2;
		double deltaYFrom = UtilsSize.computeRectangleHeight(assoc
				.getNodeFrom());
		// Placement horizontal.
		if (startPos.getA() <= endPos.getA()) {
			posX = startPos.getA() - deltaX;
		} else {
			posX = startPos.getA() + deltaX;
		}
		// Placement vertical.
		if (startPos.getB() <= endPos.getB()) {
			posY = startPos.getB() + deltaYFrom;
		} else {
			posY = startPos.getB() - deltaYFrom;
		}
		return new Duo<Double, Double>(posX, posY);
	}

	/**
	 * Getter de la position des fleches.
	 * 
	 * @param startPos
	 * @param endPos
	 * @return
	 */
	protected Duo<Duo<Double, Double>, Duo<Double, Double>> getPosition(
			Duo<Integer, Integer> startPos, Duo<Integer, Integer> endPos,
			ModelAssociation asso) {
		double heightFrom = UtilsSize
				.computeRectangleHeight(asso.getNodeFrom());
		double heightTo = UtilsSize.computeRectangleHeight(asso.getNodeTo());
		Double posXFrom;
		Double posXTo;
		// Check positionHorizontale.
		// From à gauche de TO
		if (startPos.getA() < endPos.getA()) {
			if (endPos.getA() - startPos.getA() > PropertiesManager
					.getInstance().getDoubleProperty(PROPERTY_RECTANGLE_WIDTH) / 2) {
				posXFrom = startPos.getA()
						+ PropertiesManager.getInstance().getDoubleProperty(
								PROPERTY_RECTANGLE_WIDTH) / 2;
				posXTo = endPos.getA()
						- PropertiesManager.getInstance().getDoubleProperty(
								PROPERTY_RECTANGLE_WIDTH) / 2;
			} else {
				double pc = (endPos.getA() - startPos.getA())
						/ PropertiesManager.getInstance().getDoubleProperty(
								PROPERTY_RECTANGLE_WIDTH);
				posXFrom = startPos.getA()
						+ PropertiesManager.getInstance().getDoubleProperty(
								PROPERTY_RECTANGLE_WIDTH) * pc;
				posXTo = endPos.getA()
						- PropertiesManager.getInstance().getDoubleProperty(
								PROPERTY_RECTANGLE_WIDTH) * pc;
			}
		}
		// From à droite de TO
		else if (startPos.getA() > endPos.getA()) {
			if (startPos.getA() - endPos.getA() > PropertiesManager
					.getInstance().getDoubleProperty(PROPERTY_RECTANGLE_WIDTH) / 2) {
				posXFrom = startPos.getA()
						- PropertiesManager.getInstance().getDoubleProperty(
								PROPERTY_RECTANGLE_WIDTH) / 2;
				posXTo = endPos.getA()
						+ PropertiesManager.getInstance().getDoubleProperty(
								PROPERTY_RECTANGLE_WIDTH) / 2;
			} else {
				double pc = (startPos.getA() - endPos.getA())
						/ PropertiesManager.getInstance().getDoubleProperty(
								PROPERTY_RECTANGLE_WIDTH);
				posXFrom = startPos.getA()
						- PropertiesManager.getInstance().getDoubleProperty(
								PROPERTY_RECTANGLE_WIDTH) * pc;
				posXTo = endPos.getA()
						+ PropertiesManager.getInstance().getDoubleProperty(
								PROPERTY_RECTANGLE_WIDTH) * pc;
			}
		}
		// Same xPos.
		else {
			posXFrom = startPos.getA().doubleValue();
			posXTo = endPos.getA().doubleValue();
		}

		Double posYFrom;
		Double posYTo;
		// Check positionVerticale.
		// From en dessous de TO
		if (startPos.getB() < endPos.getB()) {
			if (endPos.getB() - startPos.getB() > Math
					.max(heightFrom, heightTo)) {
				posYFrom = startPos.getB() + heightFrom / 2;
				posYTo = endPos.getB() - heightTo / 2;
			} else {
				double pcFrom = (endPos.getB() - startPos.getB()) / heightFrom;
				double pcTo = (endPos.getB() - startPos.getB()) / heightTo;
				posYFrom = startPos.getB() + heightFrom * pcFrom;
				posYTo = endPos.getB() - heightTo * pcTo;
			}
		}
		// From au dessus de TO
		else if (startPos.getB() > endPos.getB()) {
			if (startPos.getB() - endPos.getB() > Math
					.max(heightFrom, heightTo)) {
				posYFrom = startPos.getB() - heightFrom / 2;
				posYTo = endPos.getB() + heightTo / 2;
			} else {
				double pcFrom = (endPos.getB() - startPos.getB()) / heightFrom;
				double pcTo = (endPos.getB() - startPos.getB()) / heightTo;
				posYFrom = startPos.getB() - heightFrom * pcFrom;
				posYTo = endPos.getB() + heightTo * pcTo;
			}
		}
		// Same yPos.
		else {
			posYFrom = startPos.getB().doubleValue();
			posYTo = endPos.getB().doubleValue();
		}
		return new Duo<Duo<Double, Double>, Duo<Double, Double>>(
				new Duo<Double, Double>(posXFrom, posYFrom),
				new Duo<Double, Double>(posXTo, posYTo));
	}
}
